智能指针
C++ 标准库(STL)中
头文件:#include <memory>
C++ 98
std::auto_ptr<std::string> ps (new std::string(str));
C++ 11
- shared_ptr
- unique_ptr
- weak_ptr
- auto_ptr(被 C++11 弃用)
- Class shared_ptr 实现共享式拥有(shared ownership)概念。多个智能指针指向相同对象,该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作,标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。
- Class unique_ptr 实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏(resource leak)——如 new 后忘记 delete ——特别有用。
shared_ptr
- 共享资源所有权的指针。
多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。
- 支持定制型删除器(custom deleter),可防范 Cross-DLL 问题(对象在动态链接库(DLL)中被 new 创建,却在另一个 DLL 内被 delete 销毁)、自动解除互斥锁
weak_ptr
- 共享资源的观察者,需要和 std::shared_ptr 一起使用,不影响资源的生命周期。
weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。
- 可打破环状引用(cycles of references,两个其实已经没有被使用的对象彼此互指,使之看似还在 “被使用” 的状态)的问题
unique_ptr
- 独占资源所有权的指针。
unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有着被销毁或编程 empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。
- unique_ptr 用于取代 auto_ptr
auto_ptr
被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 std::move
语义,以及其他瑕疵。
auto_ptr 与 unique_ptr 比较
- auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了
move
语义; - auto_ptr 对象不能管理数组(析构调用
delete
),unique_ptr 可以管理数组(析构调用delete[]
);
补充
- Aliasing constructor
简单说就是构造出来的 shared_ptr 对象和参数 r 指向同一个控制块(会影响 r 指向的资源的生命周期),但是指向共享资源的指针是参数 ptr。看下面这个例子。
// shared_ptr constructor example
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
using Vec = std::vector<int>;
std::shared_ptr<int> GetSPtr() {
auto elts = {0, 1, 2, 3, 4};
std::shared_ptr<Vec> pvec = std::make_shared<Vec>(elts); //use_count == 1
std::shared_ptr<int> t(pvec, &(*pvec)[2]); //use_count == 2
pvec.reset(); // use_count == 1
return t;
}
int main() {
std::shared_ptr<int> sptr = GetSPtr();// use_count == 1 ,t,pvec都销毁,sptr引用
sptr.reset();// use_count == 1
cout<<*sptr.get()<<endl; // =2 指向的是&(*pvec)[2],并且延长了pvec指向vector<int>的生命周期
return 0;
}